package com.myk.game.gobangchess.ai.startzone;

import java.util.Arrays;
import java.util.Random;

public class GA_ALT {

    public GA_ALT_Result GA_ALT_1(int[][] base_board, int color, int numberOfZeros, int remain_num, int checking_num, int popSize, int maxGen) {
        int n = numberOfZeros;
        int m = remain_num;
        double crossoverRate = 0.35;
        double mutationRate = 0.35;

        int[][] topGenes = new int[0][];
        int[] topFitnesses = new int[0];
        int topFitnesses_value = -1;
        int length_topFitnesses = 0;

        int[][] population = new int[popSize][n];
        for (int i = 0; i < popSize; i++) {
            Arrays.fill(population[i], 0);
            int[] idx = randperm(n, m);
            for (int index : idx) {
                population[i][index] = 1;
            }
        }

        for (int gen = 0; gen < maxGen; gen++) {
            int[] fitness = new int[popSize];
            for (int i = 0; i < popSize; i++) {
                fitness[i] = FitnessCalculator.getFitnessCodes(population[i], base_board, color);
            }

            for (int i = 0; i < popSize; i++) {
                int[] currentGene = population[i];
                int currentFitness = fitness[i];

                if (currentFitness >= topFitnesses_value) {
                    if (currentFitness == topFitnesses_value && length_topFitnesses < checking_num) {
                        boolean exists = false;
                        for (int[] gene : topGenes) {
                            if (Arrays.equals(gene, currentGene)) {
                                exists = true;
                                break;
                            }
                        }
                        if (!exists) {
                            topGenes = Arrays.copyOf(topGenes, topGenes.length + 1);
                            topGenes[topGenes.length - 1] = currentGene;
                            topFitnesses = Arrays.copyOf(topFitnesses, topFitnesses.length + 1);
                            topFitnesses[topFitnesses.length - 1] = currentFitness;
                            length_topFitnesses = topFitnesses.length;
                        }
                    } else if (currentFitness > topFitnesses_value) {
                        topGenes = new int[][]{currentGene};
                        topFitnesses = new int[]{currentFitness};
                        length_topFitnesses = 1;
                        topFitnesses_value = currentFitness;
                    }
                }
            }

            int[][] selected = SelectionAlgorithm.selection(population, fitness);
            int[][] children = CrossoverAlgorithm.crossover(selected, crossoverRate, n, m, base_board, color);
            population = MutationAlgorithm.mutation(children, mutationRate, n, m, base_board, color);
        }

        int[] topFitnessesSorted = topFitnesses;
        int[][] topGenesSorted = topGenes;

        return new GA_ALT_Result(topFitnessesSorted, topGenesSorted);
    }

    private int[] randperm(int n, int m) {
        int[] result = new int[m];
        int[] numbers = new int[n];
        for (int i = 0; i < n; i++) {
            numbers[i] = i;
        }
        for (int i = 0; i < n; i++) {
            Random rand = new Random();
            int randomIndex = i + rand.nextInt(n - i);
            int temp = numbers[randomIndex];
            numbers[randomIndex] = numbers[i];
            numbers[i] = temp;
        }
        System.arraycopy(numbers, 0, result, 0, m);
        return result;
    }

    public static class GA_ALT_Result {
        public int[] topFitnessesSorted;
        public int[][] topGenesSorted;

        public GA_ALT_Result(int[] topFitnessesSorted, int[][] topGenesSorted) {
            this.topFitnessesSorted = topFitnessesSorted;
            this.topGenesSorted = topGenesSorted;
        }
    }
    public static void main(String[] args) {
        int[][] base_board = {
            {3, 3, 3, 1, 2, 3, 3, 3, 3},
            {3, 3, 0, 1, 0, 0, 0, -1, 3},
            {3, 1, 0, 0, 0, 1, 0, 2, 3},
            {1, 0, 0, 1, 0, 0, 0, 0, 3},
            {2, 0, 0, 0, 2, 0, 0, 0, -1},
            {3, 0, 0, 2, 0, 1, 0, 0, 2},
            {3, 0, 1, 0, 0, 0, 1, 0, 3},
            {3, 0, 0, 1, 2, 0, 2, 3, 3},
            {3, 3, 3, 3, -1, 2, 3, 3, 3}
        };
        int color = 1;
        int numberOfZeros = 33;
        int remain_num = 6;
        int checking_num = 500;
        int popSize = 200;
        int maxGen = 1000;

        GA_ALT ga = new GA_ALT();
        GA_ALT_Result result = ga.GA_ALT_1(base_board, color, numberOfZeros, remain_num, checking_num, popSize, maxGen);

        System.out.println("Top Fitness Sorted First Element: " + result.topFitnessesSorted[0]);
        System.out.println("Length of topFitnessesSorted: " + result.topFitnessesSorted.length);
        
    }
}
